/****************************************************************************
**
** Authors: Antonio Marcio A Menezes, Emanuel Grohs.
**
** This file is part of the Qt Board Game Framework.
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the
** packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "boardgame.h"

using namespace qtbf;

// protected Controle controle;

/* Primary deck of cards almost every game has. */
protected Zone deck;

/* Map of card values for the game. */
protected Map<Valores, Integer> valores;

// String nome;

// private Map<String, IPlayer> players;

// TODO mudar pra IEstado
protected GameState estado;

protected Stack<IEstado> mementos;

/* Abstract method for client implementation defines which cards will be used. */
protected abstract Zone defineDeck();

/* Abstract method for client implementation defines which zones will organize the board. */
protected abstract List<Zone> defineZones(List<IPlayer> players);

BoardGame::BoardGame(/* Controle controle/* GameState estado */)
{
    // new Controle(this, estado);
    // this.controle = Controle.getInstancia();
    valores = defineValores();
    deck = defineDeck();
    // players = new LinkedHashMap<String, IPlayer>();
}

/**
 * Valida a ação de mover retornando msg de erro quando invalida. Pode ser
 * sobreescrito invocando o super para adicionar novas msgs.
 *
 * @return Msg de erro mostrada ao user.
 */
AbsCommand BoardGame::validadeAction(AbsCommand command) {
        List<Zone> origens = command.getOrigens();
        Zone destino = command.getDestino();

        String nomePlayer = Controle.nomePlayer;
        for (Zone origem : origens) {
                if (origem == null) {
                        command.appendMsg("Zone origem nula\n");
                }
                if (origem == null) {
                        command.appendMsg("Zone destino nula\n");
                }
                if (origem.equals(destino)) {
                        command.appendMsg("Zone origem igual à destino: " + origem.getName() + "\n");
                }
                if (!origem.possuida(nomePlayer)) {// donos.contains(Player.getInstancia().getNome()))
                        command.appendMsg("Zone origem não pertence ao player: " + origem.getName() + "\n");
                }
                if (!destino.possuida(nomePlayer)) {
                        command.appendMsg("Zone destino não pertence ao player: " + destino.getName() + "\n");
                }
                if (!estado.getPlayerVez().equals(nomePlayer)) {
                        if (!((Player) players.get(estado.getPlayerVez())).getNomePlayer().equals(nomePlayer);
                        command.appendMsg("Não é sua vez\n");
                }
        }
        command = aposValidaMove(command);
        if (/* validar */true) {
                // executa(new AbsCommand(COMMAND.VALIDATE, origens, destino));
                if (command.getMsg().length() != 0) {
                        for (Zone origem : origens) {
                                if (origem.getParent() instanceof Zone) {
                                        ((Zone) origem.getParent()).reorganiza();
                                }
                        }
                        return command;
                }
        }
        return command;
}

protected AbsCommand aposValidaMove(AbsCommand command) {
        return command;
}

protected AbsCommand antesExecuta(AbsCommand command) {
        // List<AbsCommand> comandos = new ArrayList<AbsCommand>();
        // comandos.add(command);
        return command;
}

public final AbsCommand executa(AbsCommand command) {
        // List<AbsCommand> comandos =
        command = antesExecuta(command);
        // for (AbsCommand absCommand : comandos)
        {
                if (COMMAND.MOVE == command.getCommand()) {
                        command = validaMove(command);
                        if (command.getMsg().length() == 0) {
                                move(command);
                        }
                } /*
                 * else if (COMMAND.VALIDATE == command.getCommand()) {
                 * validaMove(command); }
                 */else if (COMMAND.DRAW == command.getCommand()) {
                        draw(command);
                } else if (COMMAND.UNDO == command.getCommand()) {
                        // TODO passar command?
                        undo();
                }
        }
        return aposExecuta(command);
}

protected AbsCommand aposExecuta(AbsCommand command) {
        return command;
}

/* Draw a card from one zone to another. */
private AbsCommand draw(AbsCommand command) {
        // List<Zone> origens = command.getOrigens();
        Zone destino = command.getDestino();
        List<Zone> origens = new ArrayList<Zone>();
        origens.add(deck);
        for (int i = 0; i < command.getQnt(); i++) {
                executa(new AbsCommand(COMMAND.MOVE, origens, destino));
        }
        return command;
}

/**
 * @param origens
 *            Zones a serem movida.
 * @param destino
 *            Zone à qual a Zone origem sera adicionada.
 * @param validar
 *            Se deve validar a jogada. Caso a validação falhar não move.
 * @return Se moveu com sucesso.
 */
private final AbsCommand move(AbsCommand command) {
        List<Zone> origens = command.getOrigens();
        Zone destino = command.getDestino();
        for (Zone origem : origens) {
                destino.add(origem);
                if (origem.getParent() instanceof Zone) {
                        ((Zone) origem.getParent()).reorganiza();
                }
        }
        estado.setMoveu(true);
        // Cria o memento apos o move
        addMemento(estado);
        // estado.firePropertyChange("moveu", antesMoves, estado);
        return command;
}

/* End current's player turn saving game state. */
public final void endTurn(/*List<IPlayer> players*/) {
        estado.setPlayerVez(nextPlayer(/*players*/));
        estado.setMoveu(false);
        setMemento();
        // GameState clone;
        // try {
        // clone = (GameState) estado.clone();
        // clone.setPlayerVez(nextPlayer());
        // clone.setMoveu(false);
        // addMemento(clone);
        // } catch (CloneNotSupportedException e) {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // }
}

/**
 * Invocado pelo clique do botão, define o próximo jogador a jogar. Pode ser
 * sobrescrito para definir uma ordem diferente de acordo com as regras do
 * jogo.
 *
 * @return
 */
protected int nextPlayer(/* List<IPlayer> players */) {
        // IPlayer next = null;
        // String[] playerNames = estado.getPlayerNames();
        // for (int i = 0; i < players.size(); i++) {
        // if (estado.getPlayerVez().equals(players.get(i))) {
        // if (i == players.size() - 1) {
        // next = players.get(0);
        // } else {
        // next = players.get(i + 1);
        // }
        // }
        // }
        // return next;

        // Circular
        estado.setPlayerVez(estado.getPlayerVez() + 1);
        if (estado.getPlayerVez() == estado.getnPlayers()) {
                estado.setPlayerVez(0);
        }
        return estado.getPlayerVez();
}

// public void setNomePlayer(String nome) {
// this.nomePlayer = nome;
// }
//
// public String getNomePlayer() {
// return nomePlayer;
// }

/* Default implementation for card values of deck. */
private final Map<Valores, Integer> defineValores() {
        Map<Valores, Integer> valores = new HashMap<Valores, Integer>();
        for (int i = 0; i < Valores.values().length; i++) {
                valores.put(Valores.values()[i], i);
        }
        aposDefineValores(valores);
        return valores;
}

protected void aposDefineValores(Map<Valores, Integer> valores) {

}

/* Restores last saved game state. */
final IEstado undo() {
        return mementos.pop();
}

private final void addMemento(IEstado estado) {
        /*
         * try { estado = (GameState) estado.clone(); } catch
         * (CloneNotSupportedException e) { // TODO Auto-generated catch block
         * e.printStackTrace(); }
         */
        mementos.add(estado);
}

public final void setMemento() {
        if (mementos == null) {
                mementos = new Stack<IEstado>();
        } else {
                mementos.clear();
        }
        addMemento(estado);
}

/**
 * "Host" configura o estado do jogo ao clicar play. Define os nomes dos
 * jogadores as Zones e notifica-os.
 *
 * @param playerNames
 *
 */
public final GameState configuraEstado(List<IPlayer> players) {
        // Se atingiu numero de jogadores.
        estado.setPlayerVez(0);
        // estado.setPlayerNames(playerNames);
        // TODO nullpointer se nao definiu Zones
        for (Zone Zone : defineZones(players)) {
                estado.add(Zone);
        }
        // setNumPlayers(numPlayers);
        // passaVez();
        return estado;
}

public GameState getGameState() {
        return estado;
}

/* Instantiate a brand new game state at game beginnig. */
public IEstado createGameState(Class classEstado, Integer valor) {
        try {
                this.estado = (GameState) classEstado.newInstance();
        } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
        this.estado.setnPlayers(valor);
        return this.estado;
}

public vez(){

}

public void setGameState(GameState estado) {
        this.estado = estado;
}
